package main

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"strconv"
	"time"

	etcd "github.com/coreos/etcd/client"
)

type Master struct {
	etcd.KeysAPI

	path string

	members map[string]int
}

func NewMaster(hosts []string, path string) (*Master, error) {
	config := etcd.Config{
		Endpoints: hosts,
		Transport: etcd.DefaultTransport,
		// fail fast when the target endpoint is unavailable
		HeaderTimeoutPerRequest: time.Second,
	}

	clt, err := etcd.New(config)
	if err != nil {
		return nil, err
	}

	master := &Master{
		KeysAPI: etcd.NewKeysAPI(clt),
		path:    path,
		members: make(map[string]int),
	}

	return master, nil
}

func (this *Master) GetWorker() (string, int) {
	i := rand.Intn(len(this.members))
	for host, load := range this.members {
		if i <= 0 {
			return host, load
		}
		i--
	}
	return "", 0
}

func (this *Master) WatchWorkers() {
	watcher := this.Watcher(this.path, &etcd.WatcherOptions{Recursive: true})

	for {
		rsp, err := watcher.Next(context.Background())
		if err != nil {
			log.Println("failed to watch workers:", this.path, err)
			break
		}

		switch rsp.Action {
		case "expire":
			delete(this.members, rsp.PrevNode.Key)
			fmt.Println("node:", rsp.PrevNode.Key, "expire")
		case "delete":
			delete(this.members, rsp.PrevNode.Key)
			fmt.Println("node:", rsp.PrevNode.Key, "delete")
		case "set":
			this.members[rsp.Node.Key], _ = strconv.Atoi(rsp.Node.Value)
			fmt.Println("node:", rsp.Node.Key, "set:", rsp.Node.Value)
		}
	}
}
